import { getIconByName } from "@/components/Icon";
import { ZTabItem } from "@/stores/tabs";
import { isNumber, getFullPath, insertBy } from "@/utils";
import { HOME_PAGE_ROUTE_PATH } from "@/utils/constants";
import { RouteRecordNormalized, RouteRecordRaw, RouterLink } from "vue-router";
import { routes } from ".";

export type ZMenuItem = {
  orderNo: number;
  key: string;
  label?: string | (() => JSX.Element);
  icon: any;
  children?: ZMenuItem[];
};

const RootRoute: RouteRecordRaw = { path: "/", redirect: HOME_PAGE_ROUTE_PATH };

export function getMenusFromRoutes(
  routes: RouteRecordRaw[],
  parent = RootRoute
) {
  const menus: ZMenuItem[] = [];

  let curMenu: ZMenuItem;
  for (const route of routes) {
    const {
      path,
      children,
      meta: { title, icon, orderNo, hideMenu } = { title: "--" },
    } = route;

    if (hideMenu) continue;

    const fullPath = getFullPath(path, parent.path);

    const getLabel = () => {
      if (children?.length) return title;
      return () => <RouterLink to={fullPath}> {title} </RouterLink>;
    };

    curMenu = {
      key: fullPath,
      label: getLabel(),
      icon: getIconByName(icon),
      orderNo: isNumber(orderNo) ? orderNo : 0,
    };

    if (children?.length) {
      curMenu.children = getMenusFromRoutes(children, {
        ...route,
        path: fullPath,
      });
    }

    insertBy<ZMenuItem>(curMenu, menus, "orderNo");
  }

  return menus;
}

/**
 * To flat routes tree into 2-level tree so that
 * only <ZLayout> and its direct children will be reserved in <RouterView>.
 */
export function flatRoutes(routes: RouteRecordRaw[]) {
  const _routes: RouteRecordRaw[] = [];
  for (const route of routes) {
    const { path, children } = route;

    if (children?.length) {
      route.children = flat(children, path);
    }

    _routes.push(route);
  }

  return _routes;

  function flat(routes: RouteRecordRaw[], pPath: string) {
    const res: RouteRecordRaw[] = [];
    for (const routeItem of routes) {
      if (routeItem.children?.length) {
        res.push(
          ...flat(routeItem.children, getFullPath(routeItem.path, pPath))
        );
      } else {
        routeItem.path = getFullPath(routeItem.path, pPath);
        res.push(routeItem);
      }
    }
    return res;
  }
}

/**
 * This is for menus which don't have a real view, and
 * that means this promise would never be called.
 */
export const placeHolderComponent = (componentName = "PlaceHolder") =>
  new Promise((resolve) => resolve({ name: componentName }));

function getFixedTabsFromRoutes(routes: RouteRecordRaw[], parent = RootRoute) {
  const tabs: ZTabItem[] = [];

  for (const route of routes) {
    const {
      path,
      children,
      meta: { title, icon, fixed, hideTab } = {},
    } = route;

    const fullPath = getFullPath(path, parent.path);

    if (children?.length) {
      const ctabs = getFixedTabsFromRoutes(children, {
        ...route,
        path: fullPath,
      });
      ctabs.forEach((ct) => tabs.push(ct));
      continue;
    } else if (!hideTab && fixed && title) {
      const curTab: ZTabItem = {
        fullPath,
        label: title,
        icon,
        closable: false,
      };
      tabs.push(curTab);
    }
  }

  return tabs;
}

export const getInitTabs = () => getFixedTabsFromRoutes(routes);

export function getExactMatched(matched: RouteRecordNormalized[]) {
  return matched.slice(-1)[0];
}
